/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>

#include "yaffut.h"
#include "myassert.h"
#include "TestSession.h"
#include "TestEventServer.h"
#include "TestRoom.h"

#include "MudContainer.h"
#include "MudCreature.h"
#include "MudExit.h"
#include "MudItem.h"
#include "MudCredits.h"
#include "MudObject.h"
#include "MudPlayer.h"
#include "MudWearable.h"
#include "Parser.h"
#include "ParserFactory.h"
#include "color.h"

#include "Commands.h"

using namespace std;
using namespace Chronos;


class ItemCommandsTest {
public:
    ItemCommandsTest()
        : parser(*ParserFactory::create())
		, room("RoomName")
        , session("player")
        , player(session.getPlayer())
    {
		room.addPlayer(player, "");
        session.clear();
        room.clear();
    }
    ~ItemCommandsTest() {
		room.removePlayer(player, "");
        delete &parser;
    }
	void checkResult(const char* input, const string& expected) {
		parser.parse(session, input);
		session.SESSION_CHECKWRITTEN(expected);
	}

    Parser& parser;
    TestRoom room;

    TestSession session;
    MudPlayer& player;
};


TEST(ItemCommandsTest, testPickupCommand) {
	checkResult("get", "Usage: get [item]\n");
	checkResult("get nonExist", "There is no nonExist here.\n");

	room.addObject(new MudObject("testObject", ""));
	checkResult("get testObject", "You cannot pick that up.\n");

	room.addObject(new MudItem(0, "testItem", "", 1));
	checkResult("get testItem", "You pickup testItem.\n");
	ASSERT_NULL(room.findObject("testItem"));
	ASSERT_NOT_NULL(player.findItem("testItem"));
	room.ROOM_CHECKWRITTEN("player picks up the testItem.\n");
	checkResult("get testItem", "You already have that.\n");

	//TODO test trigger
}


TEST(ItemCommandsTest, testPickupAllCommand) {
	room.addObject(new MudItem(0, "testItem1", "", 1));
	room.addObject(new MudItem(0, "testItem2", "", 1));
    MudObject* hidden = new MudItem(0, "hidden", "", 1);
    hidden->setVisible(false);
	room.addObject(hidden);
	checkResult("get all", "You pickup testItem1.\nYou pickup testItem2.\n");
	ASSERT_NULL(room.findObject("testItem1"));
	ASSERT_NULL(room.findObject("testItem2"));
	ASSERT_NOT_NULL(room.findObject("hidden"));
	ASSERT_NOT_NULL(player.findItem("testItem1"));
	ASSERT_NOT_NULL(player.findItem("testItem2"));
}


TEST(ItemCommandsTest, testDropCommand) {
	checkResult("drop", "Usage: drop [item]\n");
	checkResult("drop testItem", "You don't have a testItem.\n");

	player.addItem(new MudItem(0, "testItem", "", 1));
	checkResult("drop testItem", "dropping testItem.\n");
	ASSERT_NOT_NULL(room.findObject("testItem"));
	ASSERT_NULL(player.findItem("testItem"));
	room.ROOM_CHECKWRITTEN("player drops a testItem.\n");
}


TEST(ItemCommandsTest, testDropCommandFromEquipment) {
    MudWearable* equip = new MudWearable(0, "equip", "", 1, Equipment::head);
	player.addItem(equip);
    player.wear(equip);
    session.clear();

	checkResult("drop equip", "Please remove it first.\n");
	ASSERT_NULL(room.findObject("equip"));
	ASSERT_NULL(player.findItem("testItem"));
}


TEST(ItemCommandsTest, testDropCommandWithSameItemInEquipAndInv) {
    MudWearable* equip = new MudWearable(0, "equip", "", 1, Equipment::head);
	player.addItem(equip);
    player.wear(equip);
	player.addItem(new MudItem(0, "equip", "", 1));
    session.clear();
    room.clear();

	checkResult("drop equip", "dropping equip.\n");
	ASSERT_NOT_NULL(room.findObject("equip"));
	ASSERT_NOT_NULL(player.findItem("equip"));
	room.ROOM_CHECKWRITTEN("player drops a equip.\n");
}


TEST(ItemCommandsTest, testDropAllCommand) {
	checkResult("drop all", "Nothing to drop.\n");

	player.addItem(new MudItem(0, "testItem", "", 1));
	player.addItem(new MudItem(0, "testItem2", "", 1));
    MudWearable* helmet = new MudWearable(0, "helmet", "", 1, Equipment::head);
    player.addItem(helmet);
    player.wear(helmet);
    session.clear();
    room.clear();

	checkResult("drop all", "dropping testItem.\ndropping testItem2.\n");
	room.ROOM_CHECKWRITTEN("player drops a testItem.\nplayer drops a testItem2.\n");
	ASSERT_NOT_NULL(room.findObject("testItem"));
	ASSERT_NOT_NULL(room.findObject("testItem2"));
	ASSERT_NULL(room.findObject("helmet"));
	ASSERT_NULL(player.findItem("testItem"));
	ASSERT_NULL(player.findItem("testItem2"));
	ASSERT_NOT_NULL(player.findItem("helmet"));
}


TEST(ItemCommandsTest, testPutInCommand) {
	checkResult("put in", "Usage: put [item] in [object]\n");
	checkResult("put testItem in testContainer", "You don't have a testItem.\n");
	
	player.addItem(new MudItem(0, "testItem", "", 1));
	checkResult("put testItem in testContainer", "There is no testContainer here.\n");

	room.addObject(new MudObject("testObject", ""));
	checkResult("put testItem in testObject", "You cannot put something in that.\n");

	MudContainer* container = new MudContainer("testContainer", "");
	room.addObject(container);
	checkResult("put testItem in testContainer", "OK\n");
	room.ROOM_CHECKWRITTEN("player puts a testItem in the testContainer.\n");
	ASSERT_NOT_NULL(container->findItem("testItem"));
	ASSERT_NULL(player.findItem("testItem"));
}


TEST(ItemCommandsTest, testPutInFromEquipment) {
    MudWearable* equip = new MudWearable(0, "equip", "", 1, Equipment::head);
	player.addItem(equip);
    player.wear(equip);
    session.clear();

	MudContainer* container = new MudContainer("testContainer", "");
	room.addObject(container);

	checkResult("put equip in testContainer", "Please remove it first.\n");
	ASSERT_NOT_NULL(player.findItem("equip"));
}


TEST(ItemCommandsTest, testGetFromCommand) {
	checkResult("get from", "Usage: get [item] from [object]\n");
	checkResult("get testItem from testContainer", "There is no testContainer here.\n");
	
	room.addObject(new MudObject("testObject", ""));
	checkResult("get testItem from testObject", "You cannot get something from that.\n");

	MudContainer* container = new MudContainer("testContainer", "");
	room.addObject(container);
	checkResult("get testItem from testContainer", "There is no testItem in the testContainer.\n");

	container->addItem(new MudItem(0, "testItem", "", 1));
	checkResult("get testItem from testContainer", "You pickup testItem.\n");
	room.ROOM_CHECKWRITTEN("");
	ASSERT_NULL(container->findItem("testItem"));
	ASSERT_NOT_NULL(player.findItem("testItem"));

	//TODO test trigger
}


TEST(ItemCommandsTest, testGetAllFromCommand) {
	MudContainer* container = new MudContainer("testContainer", "");
	room.addObject(container);
	container->addItem(new MudItem(0, "testItem1", "", 1));
	container->addItem(new MudItem(0, "testItem2", "", 1));
    MudItem* hidden = new MudItem(0, "hidden", "", 1);
    hidden->setVisible(false);
	container->addItem(hidden);
	checkResult("get all from testContainer", "You pickup testItem1.\nYou pickup testItem2.\n");
	ASSERT_NULL(container->findItem("testItem1"));
	ASSERT_NULL(container->findItem("testItem2"));
	ASSERT_NOT_NULL(container->findItem("hidden"));
	ASSERT_NOT_NULL(player.findItem("testItem1"));
	ASSERT_NOT_NULL(player.findItem("testItem2"));
}


TEST(ItemCommandsTest, testGettingFromOpenableContainer) {
	MudOpenableContainer* container = new MudOpenableContainer("Container", "", 1);
	room.addObject(container);
	container->addItem(new MudItem(0, "testItem", "", 1));

	checkResult("get testItem from Container", "The Container is closed.\n");
	room.ROOM_CHECKWRITTEN("");
	ASSERT_NOT_NULL(container->findItem("testItem"));
	ASSERT_NULL(player.findItem("testItem"));

	//TODO test trigger
}


TEST(ItemCommandsTest, testWearCommand) {
	checkResult("wear", "Usage: wear [item]\n");

    MudWearable* helmet = new MudWearable(0, "helmet", "", 1, Equipment::head);
    player.addItem(helmet);

	checkResult("wear helmet", "OK\n");
    ASSERT_NULL(player.findInInventory("helmet"));
    ASSERT_NOT_NULL(player.findInEquipment("helmet"));
}


TEST(ItemCommandsTest, testWearCommandWhenAlreadyWorn) {
    MudWearable* helmet = new MudWearable(0, "helmet", "", 1, Equipment::head);
    player.addItem(helmet);
    player.wear(helmet);
    session.clear();

	checkResult("wear helmet", "You're already wearing that!\n");
}


TEST(ItemCommandsTest, testRemoveCommand) {
	checkResult("remove", "Usage: remove [item]\n");

    MudWearable* helmet = new MudWearable(0, "helmet", "", 1, Equipment::head);
    player.addItem(helmet);

	checkResult("remove helmet", "You're not wearing that.\n");
    ASSERT_NOT_NULL(player.findInInventory("helmet"));
    ASSERT_NULL(player.findInEquipment("helmet"));

    player.wear(helmet);
    session.clear();
	checkResult("remove helmet", "removing helmet.\n");
    ASSERT_NOT_NULL(player.findInInventory("helmet"));
    ASSERT_NULL(player.findInEquipment("helmet"));
}


TEST(ItemCommandsTest, testRemoveAllCommand) {
	checkResult("remove all", "You're not wearing anything.\n");

    MudWearable* helmet = new MudWearable(0, "helmet", "", 1, Equipment::head);
    player.addItem(helmet);
    player.wear(helmet);
    MudWearable* ring = new MudWearable(0, "ring", "", 1, Equipment::neck);
    player.addItem(ring);
    player.wear(ring);
    session.clear();

	checkResult("remove all", "removing helmet.\nremoving ring.\n");
	checkResult("remove all", "You're not wearing anything.\n");
}


TEST(ItemCommandsTest, testDrinkingFromEquipment) {
    MudWearable* equip = new MudWearable(0, "equip", "", 1, Equipment::head);
	player.addItem(equip);
    player.wear(equip);
    session.clear();

	checkResult("drink equip", "You cannot drink that.\n");
	ASSERT_NOT_NULL(player.findItem("equip"));
}


TEST(ItemCommandsTest, testInventoryCommand) {
    checkResult("inv", "inventory:\nYou are carrying nothing.\ncredits: 0   total weight: 0\n");

	player.addItem(new MudItem(0, "testItem", "", 11));
	player.addItem(new MudItem(0, "testItem2", "", 22));
	player.addItem(new MudCredits(0, "credits", "", 1234));
    MudWearable* helmet = new MudWearable(0, "helmet", "",33, Equipment::head);
    player.addItem(helmet);
    player.wear(helmet);
    session.clear();
    room.clear();

    checkResult("inv", "inventory:\n   testItem\n   testItem2\ncredits: 1234   total weight: 66\n");
}


